import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { In, Repository, DeepPartial } from "typeorm";
import { CharacterEntity } from "../entities/character.entity";
import { CharacterAttributeEntity } from "../entities/character-attribute.entity";
import { CharacterAttributePointsEntity } from "../entities/character-attribute-points.entity";
import { CharacterEquipmentEntity } from "../entities/character-equipment.entity";
import { CharacterMedicineEntity } from "../entities/character-medicine.entity";
import { CharacterEquipmentInventoryEntity } from "../entities/character-equipment-inventory.entity";
import { CharacterMountEntity } from "../entities/character-mount.entity";
import { EquipmentGemSocketEntity } from "../entities/equipment-gem-socket.entity";
import { GemAttributeEntity } from "../entities/gem-attribute.entity";

@Injectable()
export class CharacterAttributeService {
  constructor(
    @InjectRepository(CharacterEntity)
    private characterRepository: Repository<CharacterEntity>,
    @InjectRepository(CharacterAttributeEntity)
    private attributeRepository: Repository<CharacterAttributeEntity>,
    @InjectRepository(CharacterAttributePointsEntity)
    private attributePointsRepository: Repository<CharacterAttributePointsEntity>,
    @InjectRepository(CharacterEquipmentEntity)
    private equipmentRepository: Repository<CharacterEquipmentEntity>,
    @InjectRepository(CharacterMedicineEntity)
    private medicineRepository: Repository<CharacterMedicineEntity>,
    @InjectRepository(CharacterEquipmentInventoryEntity)
    private equipmentInventoryRepository: Repository<CharacterEquipmentInventoryEntity>,
    @InjectRepository(CharacterMountEntity)
    private mountRepository: Repository<CharacterMountEntity>,
    @InjectRepository(EquipmentGemSocketEntity)
    private equipmentGemSocketRepository: Repository<EquipmentGemSocketEntity>,
    @InjectRepository(GemAttributeEntity)
    private gemAttributeRepository: Repository<GemAttributeEntity>
  ) {}

  /**
   * 根据职业类型和等级计算基础属性
   */
  private calculateBaseAttributes(
    roleType: number,
    level: number,
    trans: number
  ) {
    // 角色初值
    const baseValues: Record<
      number,
      { hp: number; mp: number; ap: number; sp: number }
    > = {
      1: { hp: 360, mp: 200, ap: 80, sp: 10 }, // 武士
      2: { hp: 330, mp: 280, ap: 70, sp: 10 }, // 文人
      3: { hp: 300, mp: 360, ap: 60, sp: 10 }, // 异人
    };

    const base = baseValues[roleType] || baseValues[1];

    // 等级系数计算: [20+4x(Lv/5)+(Lv%5-1)]
    const levelDiv5 = Math.floor(level / 5);
    const levelMod5 = level % 5;
    const levelCoeff = 20 + 4 * levelDiv5 + Math.max(0, levelMod5 - 1);

    return {
      base,
      levelCoeff,
    };
  }

  /**
   * 计算角色属性（包含装备加成）
   */
  async calculateCharacterAttributes(characterId: number) {
    const character = await this.characterRepository.findOne({
      where: { id: characterId },
      relations: ["attributes", "attributePoints", "equipment"],
    });

    if (!character) {
      throw new Error("角色不存在");
    }

    const { base, levelCoeff } = this.calculateBaseAttributes(
      character.roleType,
      character.lvl,
      character.trans
    );

    // 获取属性点分配
    const attributePoints = character.attributePoints || {
      strength: 0,
      constitution: 0,
      intelligence: 0,
      agility: 0,
    };

    // 计算当前属性点：当前等级 + 已分配此属性的属性点
    const baseStrength = character.lvl + attributePoints.strength;
    const baseConstitution = character.lvl + attributePoints.constitution;
    const baseIntelligence = character.lvl + attributePoints.intelligence;
    const baseAgility = character.lvl + attributePoints.agility;

    // 获取装备加成（用于hp/mp/ap/df），负重单独计算
    const equipmentBonus = await this.calculateEquipmentBonus(characterId);

    // 根据职业计算属性
    let hp, mp, ap, sp;

    switch (character.roleType) {
      case 1: // 武士
        hp =
          Math.floor(1.1 * levelCoeff * baseConstitution) +
          base.hp +
          equipmentBonus.hp;
        mp =
          Math.floor(0.6 * levelCoeff * baseIntelligence) +
          base.mp +
          equipmentBonus.mp;
        ap =
          Math.floor(0.26 * levelCoeff * baseStrength) +
          base.ap +
          equipmentBonus.ap;
        sp = Math.floor(1.0 * (baseAgility + base.sp)) + equipmentBonus.sp;
        break;
      case 2: // 文人
        hp =
          Math.floor(1.1 * levelCoeff * baseConstitution) +
          base.hp +
          equipmentBonus.hp;
        mp =
          Math.floor(1.0 * levelCoeff * baseIntelligence) +
          base.mp +
          equipmentBonus.mp;
        ap =
          Math.floor(0.2 * levelCoeff * baseStrength) +
          base.ap +
          equipmentBonus.ap;
        // 文人速度根据转职次数调整
        const speedMultiplier = 0.8 + character.trans * 0.05;
        sp =
          Math.floor(speedMultiplier * (baseAgility + base.sp)) +
          equipmentBonus.sp;
        break;
      case 3: // 异人
        hp =
          Math.floor(1.0 * levelCoeff * baseConstitution) +
          base.hp +
          equipmentBonus.hp;
        mp =
          Math.floor(1.3 * levelCoeff * baseIntelligence) +
          base.mp +
          equipmentBonus.mp;
        ap =
          Math.floor(0.14 * levelCoeff * baseStrength) +
          base.ap +
          equipmentBonus.ap;
        sp = Math.floor(1.1 * (baseAgility + base.sp)) + equipmentBonus.sp;
        break;
      default:
        throw new Error("未知职业类型");
    }

    // 计算负重：当前负重与总负重
    const { currentWeight, maxWeight } = await this.computeWeights(characterId);

    // 叠加出战坐骑属性
    try {
      const activeMount = await this.mountRepository.findOne({
        where: { characterId, status: 2 as any },
      });
      if (activeMount) {
        // 坐骑字段含义：currentHp/Ap 为气血/攻击；
        // 速度存储在 currentMp；精力存储在 currentSp
        hp += Number((activeMount as any).currentHp || 0);
        ap += Number((activeMount as any).currentAp || 0);
        sp += Number((activeMount as any).currentMp || 0);
        mp += Number((activeMount as any).currentSp || 0);
      }
    } catch {}

    // 计算最大血蓝
    const computedMaxHp = Math.max(1, hp);
    const computedMaxMp = Math.max(1, mp);

    // 读取并限制当前血蓝（为后续战斗消耗保留当前值）
    const currentHpRaw =
      character.attributes && typeof character.attributes.hp === "number"
        ? character.attributes.hp
        : computedMaxHp;
    const currentMpRaw =
      character.attributes && typeof character.attributes.mp === "number"
        ? character.attributes.mp
        : computedMaxMp;

    const clampedHp = Math.max(0, Math.min(currentHpRaw, computedMaxHp));
    const clampedMp = Math.max(0, Math.min(currentMpRaw, computedMaxMp));

    return {
      hp: clampedHp,
      maxHp: computedMaxHp,
      mp: clampedMp,
      maxMp: computedMaxMp,
      ap: Math.max(1, ap),
      df: equipmentBonus.defense,
      sp: Math.max(1, sp),
      weight: currentWeight,
      maxWeight,
    };
  }

  /**
   * 计算角色当前抗性总和（人物本身 + 穿戴装备随机抗性 + 装备镶嵌宝石抗性）
   * 返回结构对齐1006：k_wl,k_wei,k_feng,k_fa,k_du,k_xi,k_luan,k_yao,k_ll 以及 r_* 保持人物本身
   */
  async calculateAggregatedResistances(characterId: number) {
    const character = await this.characterRepository.findOne({
      where: { id: characterId },
      relations: [
        "detailedAttributes",
        "equipment",
        "equipment.rightHandEquipment",
        "equipment.rightHandEquipment.equipmentBase",
        "equipment.bodyEquipment",
        "equipment.bodyEquipment.equipmentBase",
        "equipment.headEquipment",
        "equipment.headEquipment.equipmentBase",
        "equipment.neckEquipment",
        "equipment.neckEquipment.equipmentBase",
        "equipment.leftHandEquipment",
        "equipment.leftHandEquipment.equipmentBase",
        "equipment.footEquipment",
        "equipment.footEquipment.equipmentBase",
      ],
    });

    if (!character) {
      throw new Error("角色不存在");
    }

    const base = character.detailedAttributes || ({} as any);
    // 初始化为人物本身
    const result = {
      k_wl: Number(base.k_wl || 0),
      k_wei: Number(base.k_wei || 0),
      k_feng: Number(base.k_feng || 0),
      k_fa: Number(base.k_fa || 0),
      k_du: Number(base.k_du || 0),
      k_xi: Number(base.k_xi || 0),
      k_luan: Number(base.k_luan || 0),
      k_yao: Number(base.k_yao || 0),
      k_ll: Number(base.k_ll || 0),
      // 其余概率类字段暂沿用人物本身（未纳入装备与宝石计算范围）
      r_bj: Number(base.r_bj || 0),
      r_fj: Number(base.r_fj || 0),
      r_fz: Number(base.r_fz || 0),
      r_hb: Number(base.r_hb || 0),
      r_mf: Number(base.r_mf || 0),
      r_mz: Number(base.r_mz || 0),
      r_zm: Number(base.r_zm || 0),
    };

    // 抗性键集合
    const validKeys = new Set([
      "k_wl",
      "k_wei",
      "k_feng",
      "k_fa",
      "k_du",
      "k_xi",
      "k_luan",
      "k_yao",
      "k_ll",
    ] as (keyof typeof result)[]);

    // 概率类键集合
    const validRateKeys = new Set<keyof typeof result>([
      "r_bj",
      "r_fj",
      "r_fz",
      "r_hb",
      "r_mf",
      "r_mz",
      "r_zm",
    ]);

    // 数字类型映射（来自宝石属性type或数值化的随机抗性码）
    const mapNumericTypeToKey = (
      type?: number | null
    ): keyof typeof result | null => {
      if (type == null) return null;
      switch (Number(type)) {
        case 33:
          return "k_wl"; // 抗物理
        case 34:
          return "k_wei"; // 抗围困
        case 35:
          return "k_luan"; // 抗扰乱
        case 36:
          return "k_feng"; // 抗封锁
        case 37:
          return "k_fa"; // 抗风沙/抗风系
        case 38:
          return "k_yao"; // 抗妖火/抗妖系
        case 39:
          return "k_ll"; // 抗落雷/抗雷系
        case 40:
          return "k_du"; // 抗毒术/抗毒系
        default:
          return null;
      }
    };

    // 数字类型映射到概率类
    const mapNumericTypeToRateKey = (
      type?: number | null
    ): keyof typeof result | null => {
      if (type == null) return null;
      switch (Number(type)) {
        case 7:
          return "r_bj"; // 暴击率
        case 12:
          return "r_fj"; // 反击率
        case 23:
          return "r_fz"; // 反震率
        case 10:
          return "r_hb"; // 躲避/闪避率
        case 27:
          return "r_mf"; // 法术暴击率
        case 9:
          return "r_mz"; // 命中率
        case 22:
          return "r_zm"; // 致命率
        default:
          return null;
      }
    };

    // 文本名称映射（中文或简写或key本身）
    const mapTextToKey = (name?: string | null): keyof typeof result | null => {
      if (!name) return null;
      const raw = String(name);
      const n = raw.toLowerCase();
      // 短名精确映射（来自 short_name，例如：物/封/乱/...）
      const shortExact: Record<string, keyof typeof result> = {
        物: "k_wl",
        围: "k_wei",
        封: "k_feng",
        风: "k_fa",
        沙: "k_fa",
        毒: "k_du",
        玄: "k_xi",
        乱: "k_luan",
        妖: "k_yao",
        雷: "k_ll",
      };
      if (shortExact[raw]) return shortExact[raw];
      // 直接是标准键
      if (validKeys.has(n as any)) return n as keyof typeof result;
      // 中文/关键字匹配
      if (n.includes("物")) return "k_wl";
      if (n.includes("围") || n.includes("困")) return "k_wei";
      if (n.includes("封")) return "k_feng";
      if (n.includes("风") || n.includes("沙")) return "k_fa";
      if (n.includes("毒")) return "k_du";
      if (n.includes("玄")) return "k_xi";
      if (n.includes("乱") || n.includes("扰")) return "k_luan";
      if (n.includes("妖")) return "k_yao";
      if (n.includes("雷")) return "k_ll";
      // 数字字符串尝试按数值映射
      const maybeNum = Number(n);
      if (!Number.isNaN(maybeNum)) return mapNumericTypeToKey(maybeNum);
      return null;
    };

    // 文本到概率类键
    const mapTextToRateKey = (
      name?: string | null
    ): keyof typeof result | null => {
      if (!name) return null;
      const raw = String(name);
      const n = raw.toLowerCase();
      const rateExact: Record<string, keyof typeof result> = {
        暴: "r_bj",
        暴击: "r_bj",
        反击: "r_fj",
        反震: "r_fz",
        躲: "r_hb",
        躲避: "r_hb",
        闪避: "r_hb",
        法爆: "r_mf",
        命中: "r_mz",
        致命: "r_zm",
      };
      if (rateExact[raw]) return rateExact[raw];
      if (validRateKeys.has(n as any)) return n as keyof typeof result;
      if (n.includes("暴击")) return "r_bj";
      if (n.includes("反击")) return "r_fj";
      if (n.includes("反震")) return "r_fz";
      if (n.includes("躲避") || n.includes("闪避") || n.includes("闪"))
        return "r_hb";
      if (n.includes("法爆")) return "r_mf";
      if (n.includes("命中")) return "r_mz";
      if (n.includes("致命")) return "r_zm";
      const maybeNum = Number(n);
      if (!Number.isNaN(maybeNum)) return mapNumericTypeToRateKey(maybeNum);
      return null;
    };

    // 收集已穿戴装备（六部位）
    const equipmentSlots: any[] = [
      character.equipment?.rightHandEquipment,
      character.equipment?.bodyEquipment,
      character.equipment?.headEquipment,
      character.equipment?.neckEquipment,
      character.equipment?.leftHandEquipment,
      character.equipment?.footEquipment,
    ].filter(Boolean);

    // 叠加装备随机抗性（优先用背包记录，没有则回落到基础表）
    for (const item of equipmentSlots) {
      // 背包记录自带的随机抗性
      if (item.resistanceType && Number(item.resistanceValue || 0) > 0) {
        const key = mapTextToKey(item.resistanceType);
        if (key) {
          result[key] += Number(item.resistanceValue || 0);
        }
      } else if (item.equipmentBase) {
        // 基础表上定义的抗性
        const key = mapTextToKey(item.equipmentBase.resistance_type as any);
        if (key) {
          result[key] += Number(item.equipmentBase.resistance_value || 0);
        }
      }
    }

    // 叠加宝石抗性：查询这些已穿戴装备对应的宝石槽
    const equippedInventoryIds = equipmentSlots
      .map((i: any) => i.id)
      .filter(Boolean);
    try {
      console.log(
        "[1006] equippedInventoryIds:",
        JSON.stringify(equippedInventoryIds)
      );
    } catch {}
    if (equippedInventoryIds.length > 0) {
      const sockets = await this.equipmentGemSocketRepository.find({
        where: {
          characterId,
          equipmentId: In(equippedInventoryIds),
        },
        order: { socketSlot: "ASC" },
      });
      try {
        console.log(
          "[1006] sockets count:",
          sockets?.length || 0,
          "first:",
          sockets && sockets[0]
            ? {
                equipmentId: (sockets[0] as any).equipmentId,
                gemAttributeType: (sockets[0] as any).gemAttributeType,
                gemAttributeValue: (sockets[0] as any).gemAttributeValue,
                gemName: (sockets[0] as any).gemName,
              }
            : null
        );
      } catch {}

      // 预取宝石属性短名/全名映射，参考1423使用short_name，同时带出attribute_name
      const typeIds = Array.from(
        new Set(
          (sockets || [])
            .map((s: any) => Number(s.gemAttributeType))
            .filter((n) => !Number.isNaN(n))
        )
      );
      const idToShort = new Map<number, string>();
      const idToAttrName = new Map<number, string>();
      if (typeIds.length) {
        const rows: any[] = await this.gemAttributeRepository.query(
          `SELECT id, short_name, attribute_name FROM gem_attributes WHERE id IN (${typeIds
            .map(() => "?")
            .join(",")})`,
          typeIds
        );
        for (const r of rows) {
          idToShort.set(Number(r.id), String(r.short_name || ""));
          idToAttrName.set(Number(r.id), String(r.attribute_name || ""));
        }
      }

      // 基于 attribute_name/short_name 生成别名映射（ID -> k_* 或 r_*）
      const aliasIdToKey = new Map<number, keyof typeof result>();
      const aliasIdToRateKey = new Map<number, keyof typeof result>();
      const allIds = Array.from(
        new Set([...idToShort.keys(), ...idToAttrName.keys()])
      );
      for (const id of allIds) {
        const nm = idToAttrName.get(id) || idToShort.get(id) || "";
        const k = mapTextToKey(nm);
        if (k && validKeys.has(k)) aliasIdToKey.set(id, k);
        const rk = mapTextToRateKey(nm);
        if (rk && validRateKeys.has(rk)) aliasIdToRateKey.set(id, rk);
      }

      // 兼容：同一字段可能对应多个数值ID（例如 抗物理 = 1 或 6）
      const numericAliasToKey = new Map<number, keyof typeof result>([
        [1, "k_wl"],
        [6, "k_wl"],
      ]);

      for (const s of sockets) {
        const t = (s as any).gemAttributeType;
        const v = Number((s as any).gemAttributeValue || 0);
        // 先尝试概率类（优先避免被“封/风”等字样误归类到k_*）
        let rKey =
          aliasIdToRateKey.get(Number(t)) || mapNumericTypeToRateKey(t);
        if (!rKey) rKey = mapTextToRateKey(idToShort.get(t) || undefined);
        if (!rKey) rKey = mapTextToRateKey((s as any).gemAttributeName);
        if (!rKey) rKey = mapTextToRateKey((s as any).gemName);
        if (rKey && validRateKeys.has(rKey)) {
          result[rKey] += v;
          continue;
        }
        // 再尝试抗性类键
        let key =
          aliasIdToKey.get(Number(t)) ||
          mapNumericTypeToKey(t) ||
          numericAliasToKey.get(Number(t)) ||
          null;
        if (!key) key = mapTextToKey(idToShort.get(t) || undefined);
        if (!key) key = mapTextToKey((s as any).gemAttributeName);
        if (!key) key = mapTextToKey((s as any).gemName);
        if (key && validKeys.has(key)) {
          result[key] += v;
        }
      }
    }

    try {
      console.log("[1006] aggregated resistances:", result);
    } catch {}

    return result;
  }

  /**
   * 计算装备加成（不含负重容量、仅属性与防御、重量供参考）
   */
  private async calculateEquipmentBonus(characterId: number) {
    const equipment = await this.equipmentRepository.findOne({
      where: { characterId },
      relations: [
        "rightHandEquipment",
        "bodyEquipment",
        "headEquipment",
        "neckEquipment",
        "leftHandEquipment",
        "footEquipment",
        "rightHandEquipment.equipmentBase",
        "bodyEquipment.equipmentBase",
        "headEquipment.equipmentBase",
        "neckEquipment.equipmentBase",
        "leftHandEquipment.equipmentBase",
        "footEquipment.equipmentBase",
      ],
    });

    if (!equipment) {
      return { hp: 0, mp: 0, ap: 0, sp: 0, defense: 0, weight: 0 };
    }

    let bonus = { hp: 0, mp: 0, ap: 0, sp: 0, defense: 0, weight: 0 };

    const equipmentSlots: any[] = [
      equipment.rightHandEquipment,
      equipment.bodyEquipment,
      equipment.headEquipment,
      equipment.neckEquipment,
      equipment.leftHandEquipment,
      equipment.footEquipment,
    ];

    for (const item of equipmentSlots) {
      if (item) {
        bonus.hp += item.bonusHp || 0;
        bonus.mp += item.bonusMp || 0;
        bonus.ap += item.bonusAttack || 0;
        bonus.defense += item.bonusDefense || 0;
        bonus.weight += item.weight || 0;

        const base = item.equipmentBase;
        if (base) {
          if (!item.bonusHp) bonus.hp += base.bonus_hp || 0;
          if (!item.bonusMp) bonus.mp += base.bonus_mp || 0;
          if (!item.bonusAttack) bonus.ap += base.bonus_attack || 0;
          if (!item.bonusDefense) bonus.defense += base.bonus_defense || 0;
          if (!item.weight) bonus.weight += base.weight || 0;
        }
      }
    }

    return bonus;
  }

  /**
   * 计算当前负重与总负重
   * - 总负重: 默认50 + 力量(等级+已分配)*2
   * - 当前负重: 已穿戴装备重量之和 + 背包装备重量 + 药品数量合计(每个1)
   */
  async computeWeights(
    characterId: number
  ): Promise<{ currentWeight: number; maxWeight: number }> {
    const character = await this.characterRepository.findOne({
      where: { id: characterId },
      relations: ["attributePoints"],
    });
    if (!character) throw new Error("角色不存在");

    const attrPts = character.attributePoints || {
      strength: 0,
      constitution: 0,
      intelligence: 0,
      agility: 0,
    };
    const strength = character.lvl + attrPts.strength;
    const maxWeight = 50 + strength * 2;

    // 穿戴装备重量
    const equip = await this.equipmentRepository.findOne({
      where: { characterId },
      relations: [
        "rightHandEquipment",
        "bodyEquipment",
        "headEquipment",
        "neckEquipment",
        "leftHandEquipment",
        "footEquipment",
        "rightHandEquipment.equipmentBase",
        "bodyEquipment.equipmentBase",
        "headEquipment.equipmentBase",
        "neckEquipment.equipmentBase",
        "leftHandEquipment.equipmentBase",
        "footEquipment.equipmentBase",
      ],
    });
    let equippedWeight = 0;
    if (equip) {
      const slots: any[] = [
        equip.rightHandEquipment,
        equip.bodyEquipment,
        equip.headEquipment,
        equip.neckEquipment,
        equip.leftHandEquipment,
        equip.footEquipment,
      ];
      for (const item of slots) {
        if (!item) continue;
        equippedWeight += item.weight || item.equipmentBase?.weight || 0;
      }
    }

    // 背包装备重量（status=1 的装备记录）
    const bagEquip = await this.equipmentInventoryRepository.find({
      where: { characterId: characterId, status: 1 as any },
      relations: ["equipmentBase"],
    });
    let bagEquipWeight = 0;
    for (const e of bagEquip) {
      bagEquipWeight += e.weight || e.equipmentBase?.weight || 0;
    }

    // 药品重量：每种数量相加（每个1）
    const meds = await this.medicineRepository.find({
      where: { characterId: characterId, status: 1 as any },
    });
    let medicineWeight = 0;
    for (const m of meds) medicineWeight += m.num || 0;

    const currentWeight = equippedWeight + bagEquipWeight + medicineWeight;
    return { currentWeight, maxWeight };
  }

  /**
   * 分配属性点
   */
  async allocateAttributePoints(
    characterId: number,
    strength: number = 0,
    constitution: number = 0,
    intelligence: number = 0,
    agility: number = 0
  ) {
    const character = await this.characterRepository.findOne({
      where: { id: characterId },
      relations: ["attributePoints"],
    });

    if (!character) {
      throw new Error("角色不存在");
    }

    // 获取或创建属性点记录
    let attributePoints = character.attributePoints;
    if (!attributePoints) {
      attributePoints = this.attributePointsRepository.create({
        characterId,
        strength: 0,
        constitution: 0,
        intelligence: 0,
        agility: 0,
        availablePoints: 0,
      });
    }

    // 计算总分配点数
    const totalAllocated = strength + constitution + intelligence + agility;

    if (totalAllocated > attributePoints.availablePoints) {
      throw new Error("属性点不足");
    }

    // 更新属性点
    attributePoints.strength += strength;
    attributePoints.constitution += constitution;
    attributePoints.intelligence += intelligence;
    attributePoints.agility += agility;
    attributePoints.availablePoints -= totalAllocated;

    await this.attributePointsRepository.save(attributePoints);

    // 重新计算并更新角色属性
    const newAttributes = await this.calculateCharacterAttributes(characterId);

    // 更新或创建属性记录
    const existing = await this.attributeRepository.findOne({
      where: { characterId },
    });

    // 保留并限制当前血蓝值，不在加点时强制回满
    const prevHp = existing ? existing.hp : newAttributes.hp;
    const prevMp = existing ? existing.mp : newAttributes.mp;
    const hpToSave = Math.max(0, Math.min(prevHp, newAttributes.maxHp));
    const mpToSave = Math.max(0, Math.min(prevMp, newAttributes.maxMp));

    const entityToSave: DeepPartial<CharacterAttributeEntity> = existing
      ? {
          id: existing.id,
          characterId: existing.characterId,
          maxHp: newAttributes.maxHp,
          maxMp: newAttributes.maxMp,
          ap: newAttributes.ap,
          df: newAttributes.df,
          sp: newAttributes.sp,
          weight: newAttributes.weight,
          maxWeight: newAttributes.maxWeight,
          hp: hpToSave,
          mp: mpToSave,
        }
      : {
          characterId,
          hp: hpToSave,
          mp: mpToSave,
          maxHp: newAttributes.maxHp,
          maxMp: newAttributes.maxMp,
          ap: newAttributes.ap,
          df: newAttributes.df,
          sp: newAttributes.sp,
          weight: newAttributes.weight,
          maxWeight: newAttributes.maxWeight,
          hyd: 100,
          sinew: 100,
          p: 0,
          speedStr: "一",
        };

    const merged = this.attributeRepository.create(entityToSave);
    await this.attributeRepository.save(merged);

    return {
      attributes: newAttributes,
      attributePoints: {
        strength: attributePoints.strength,
        constitution: attributePoints.constitution,
        intelligence: attributePoints.intelligence,
        agility: attributePoints.agility,
        availablePoints: attributePoints.availablePoints,
      },
    };
  }

  /**
   * 处理角色升级时的属性点分配
   * 每升一级获得4点属性值，同时每种属性默认+1
   */
  async handleLevelUp(characterId: number, newLevel: number) {
    const character = await this.characterRepository.findOne({
      where: { id: characterId },
      relations: ["attributePoints"],
    });

    if (!character) {
      throw new Error("角色不存在");
    }

    // 获取或创建属性点记录
    let attributePoints = character.attributePoints;
    if (!attributePoints) {
      attributePoints = this.attributePointsRepository.create({
        characterId,
        strength: 0,
        constitution: 0,
        intelligence: 0,
        agility: 0,
        availablePoints: 0,
      });
    }

    // 计算应该有的总属性点（每级4点）
    const totalPoints = (newLevel - 1) * 4;
    const currentTotalPoints =
      attributePoints.strength +
      attributePoints.constitution +
      attributePoints.intelligence +
      attributePoints.agility +
      attributePoints.availablePoints;

    // 如果当前总点数不足，补充到应有的点数
    if (currentTotalPoints < totalPoints) {
      const pointsToAdd = totalPoints - currentTotalPoints;
      attributePoints.availablePoints += pointsToAdd;
    }

    // 确保每种属性至少为等级数（这里不需要修改，因为属性点分配是独立的）
    // 当前算法：当前属性点 = 等级 + 已分配属性点
    // 所以这里不需要强制设置最小值为等级数，因为等级会自动加到属性点中

    await this.attributePointsRepository.save(attributePoints);

    // 重新计算并更新角色属性
    const newAttributes = await this.calculateCharacterAttributes(characterId);

    // 更新或创建属性记录
    const existing2 = await this.attributeRepository.findOne({
      where: { characterId },
    });

    // 升级后补满血量与蓝量到最大值
    const hpToSave2 = newAttributes.maxHp;
    const mpToSave2 = newAttributes.maxMp;

    const entityToSave2: DeepPartial<CharacterAttributeEntity> = existing2
      ? {
          id: existing2.id,
          characterId: existing2.characterId,
          maxHp: newAttributes.maxHp,
          maxMp: newAttributes.maxMp,
          ap: newAttributes.ap,
          df: newAttributes.df,
          sp: newAttributes.sp,
          weight: newAttributes.weight,
          maxWeight: newAttributes.maxWeight,
          hp: hpToSave2,
          mp: mpToSave2,
        }
      : {
          characterId,
          hp: hpToSave2,
          mp: mpToSave2,
          maxHp: newAttributes.maxHp,
          maxMp: newAttributes.maxMp,
          ap: newAttributes.ap,
          df: newAttributes.df,
          sp: newAttributes.sp,
          weight: newAttributes.weight,
          maxWeight: newAttributes.maxWeight,
          hyd: 100,
          sinew: 100,
          p: 0,
          speedStr: "一",
        };

    const merged2 = this.attributeRepository.create(entityToSave2);
    await this.attributeRepository.save(merged2);

    return {
      attributes: newAttributes,
      attributePoints: {
        strength: attributePoints.strength,
        constitution: attributePoints.constitution,
        intelligence: attributePoints.intelligence,
        agility: attributePoints.agility,
        availablePoints: attributePoints.availablePoints,
      },
    };
  }

  /**
   * 获取角色基础属性信息（供指令使用）
   * 包含计算后的属性值和属性点信息
   */
  async getCharacterBasicAttributes(characterId: number) {
    const character = await this.characterRepository.findOne({
      where: { id: characterId },
      relations: ["attributePoints", "attributes"],
    });

    if (!character) {
      throw new Error("角色不存在");
    }

    // 如果没有属性点记录，创建默认记录
    if (!character.attributePoints) {
      const attributePoints = this.attributePointsRepository.create({
        characterId,
        strength: 0,
        constitution: 0,
        intelligence: 0,
        agility: 0,
        availablePoints: 0,
      });
      await this.attributePointsRepository.save(attributePoints);
      character.attributePoints = attributePoints;
    }

    // 计算角色属性
    const calculatedAttributes = await this.calculateCharacterAttributes(
      characterId
    );
    const attributePoints = character.attributePoints;

    return {
      // 角色基本信息
      id: character.id,
      name: character.name,
      roleType: character.roleType,
      sex: character.sex,
      lvl: character.lvl,
      exp: character.exp,
      trans: character.trans || 0,
      title: character.title || "",
      vip: character.vip || 0,
      headImg: character.headImg || "ws2.jpg",

      // 计算后的属性值
      hp: calculatedAttributes.hp,
      maxHp: calculatedAttributes.maxHp,
      mp: calculatedAttributes.mp,
      maxMp: calculatedAttributes.maxMp,
      ap: calculatedAttributes.ap,
      df: calculatedAttributes.df,
      sp: calculatedAttributes.sp,
      weight: calculatedAttributes.weight,
      maxWeight: calculatedAttributes.maxWeight,

      // 属性点信息（等级 + 已分配）
      app: character.lvl + attributePoints.strength, // 力量
      hpp: character.lvl + attributePoints.constitution, // 体质
      mpp: character.lvl + attributePoints.intelligence, // 智力
      spp: character.lvl + attributePoints.agility, // 敏捷
      p: attributePoints.availablePoints, // 剩余属性点

      // 其他固定属性
      hyd: 100, // 活力值
      sinew: 100, // 体力值
      speedStr: "一", // 速度等级
    };
  }

  /**
   * 获取角色属性点信息
   */
  async getAttributePoints(characterId: number) {
    const character = await this.characterRepository.findOne({
      where: { id: characterId },
      relations: ["attributePoints", "attributes"],
    });

    if (!character) {
      throw new Error("角色不存在");
    }

    // 如果没有属性点记录，创建默认记录
    if (!character.attributePoints) {
      const attributePoints = this.attributePointsRepository.create({
        characterId,
        strength: 0,
        constitution: 0,
        intelligence: 0,
        agility: 0,
        availablePoints: 0,
      });
      await this.attributePointsRepository.save(attributePoints);
      character.attributePoints = attributePoints;
    }

    const attributes = character.attributes || {};
    const attributePoints = character.attributePoints;

    return {
      // 当前属性值
      ap: attributes.ap || 80,
      hp: attributes.hp || 300,
      mp: attributes.mp || 100,
      sp: attributes.sp || 10,
      df: attributes.df || 40,
      weight: attributes.weight || 0,
      maxHp: attributes.maxHp || 300,
      maxMp: attributes.maxMp || 100,
      maxWeight: attributes.maxWeight || 100,

      // 已分配属性点（显示等级 + 已分配的点数）
      app: character.lvl + attributePoints.strength, // 力量 = 等级 + 已分配
      hpp: character.lvl + attributePoints.constitution, // 体质 = 等级 + 已分配
      mpp: character.lvl + attributePoints.intelligence, // 智力 = 等级 + 已分配
      spp: character.lvl + attributePoints.agility, // 敏捷 = 等级 + 已分配

      // 剩余属性点
      p: attributePoints.availablePoints,
    };
  }
}
